#ifndef _ORDER_H_
#define _ORDER_H_
#include "Location.h"
#include "State.h"
#include <list>
#include <algorithm>
#include <cassert>
#include "group_by.hh"
#include "Strategy.hh"

/// A possible movment option for an individual
struct IndividualOption
{
  Location from;
  Location to;
  int direction;
  double w;   // this weight indicates the importance of this options for the individual

  IndividualOption()
  {}


  IndividualOption(const Location & _from, const Location & _to, int _direction, double _w) :
    from(_from), 
    to(_to), 
    direction(_direction),
    w(_w) 
  {}
};

bool operator<(const Location & a, const Location & b);
bool operator==(const Location & a, const Location & b);
/// comparision criterion
bool same_source(const IndividualOption & a, const IndividualOption & b);
/// sort criterion
bool by_src_and_decreasing_weight(const IndividualOption & a, const IndividualOption & b);
/// comparision criterion
bool same_dest(const IndividualOption & a, const IndividualOption & b);
/// sort criterion
bool by_dest_and_decreasing_weight(const IndividualOption & a, const IndividualOption & b);
/// normalization action
void normalize_action(std::list<IndividualOption>::iterator first, std::list<IndividualOption>::iterator last);







/// solve conflicts action
struct remove_last_action_t
{
  std::list<IndividualOption> & list_ref;

  remove_last_action_t(std::list<IndividualOption> & lr) : list_ref(lr) {}

  void operator()(std::list<IndividualOption>::iterator first, std::list<IndividualOption>::iterator last)
  {
    std::list<IndividualOption>::iterator second = first;
    ++second;
    list_ref.erase(second,last);
  }
};


/// List of individual options
struct Order : public std::list<IndividualOption>
{

  /// normalizes weights by indiduals
  void normalize(void)
  {
    sort(by_src_and_decreasing_weight);
    group_by(begin(),end(),same_source,normalize_action);
  }

  /// solves conflict in a rough manner
  unsigned solve_conflicts(void)
  {

    normalize();
    sort(by_dest_and_decreasing_weight);
    remove_last_action_t remove_last_action(*this);
    
    unsigned len = size();
    group_by(begin(),end(),same_dest,remove_last_action);
    return len -size();
  }

  /// selects best combination
  unsigned  select_best(void)
  {
    // selects most promissing individual options
    remove_last_action_t remove_last_action(*this);
    sort(by_src_and_decreasing_weight);

    unsigned len = size();
    group_by(begin(),end(),same_source, remove_last_action);

    return len - size();
  }


  void proposeMove(State & s, const Location &from, const Location &to, int direction, double w);

  void execute(State & s, bool simulation = false);

};

#endif /* _ORDER_H_ */
